#ifndef __YUV__READER__H__
#define __YUV__READER__H__

#include <string>
#include <vector>
#include "RendererCommon.h"

namespace simple_renderer{

class YUVReader{
public:
    YUVReader() : file(nullptr){};
    virtual ~YUVReader(){
        LOGI("[YUVReader] delete YUVReader", frame_positions.size());
        DeInit();
    }

    int InitReader(const char* file_name){
        return InitY4m(file_name);
    }

    int InitY4m(const char* file_name){

        DeInit();

        file = fopen(file_name, "rb");
        if (file == nullptr) {
            LOGE("[YUVReader] Could not open file... %s", file_name);
            return -1;
        }
        int parse_file_header_result = -1;
        if (fscanf(file, "YUV4MPEG2 %n", &parse_file_header_result) != 0 ||
            parse_file_header_result == -1) {
            LOGE("[YUVReader] File %s does not start with YUV4MPEG2 header", file_name);
            return -1;
        }
        std::string header_line;
        while (true) {
            const int c = fgetc(file);
            if (c == EOF) {
                LOGE("[YUVReader] Could not read header line");
                return -1;
            }
            if (c == '\n')
                break;
            header_line.push_back(static_cast<char>(c));
        }

        std::vector<std::string> fields;
        tokenize(header_line, ' ', &fields);
        for (const std::string& field : fields) {
            const char prefix = field.front();
            const std::string suffix = field.substr(1);
            switch (prefix) {
                case 'W':
                    width = (int)strtod(suffix.c_str(), nullptr);
                    break;
                case 'H':
                    height = (int)strtod(suffix.c_str(), nullptr);
                    break;
                case 'C':
                    if (suffix != "420" && suffix != "420mpeg2") {
                        LOGE("[YUVReader] Does not support any other color space than I420 or 420mpeg2, but was: %s", suffix.c_str());
                        return -1;
                    }
                    break;
                case 'F': {
                    std::vector<std::string> fraction;
                    tokenize(suffix, ':', &fraction);
                    if (fraction.size() == 2) {
                        const int numerator = (int)strtod(fraction[0].c_str(), nullptr);
                        const int denominator = (int)strtod(fraction[1].c_str(), nullptr);
                        if (numerator && denominator)
                            fps = numerator / static_cast<float>(denominator);
                        break;
                    }
                }
            }
        }

        if (!width || !height) {
            LOGE( "[YUVReader] Could not find width and height in file header");
            return -1;
        }
        if (!fps) {
            LOGE("[YUVReader] Could not find fps in file header");
            return -1;
        }
        if (width % 2 != 0 || height % 2 != 0) {
            LOGE("[YUVReader] Only supports even width/height so that chroma size is a whole number.");
            return -1;
        }

        frame_size = 3 * width * height / 2;

        //std::vector<fpos_t> frame_positions;
        while (true) {
            int parse_frame_header_result = -1;
            if (fscanf(file, "FRAME\n%n", &parse_frame_header_result) != 0 ||
                parse_frame_header_result == -1) {
                if (!feof(file)) {
                    LOGI("[YUVReader] Did not find FRAME header, ignoring rest of file");
                }
                break;
            }
            fpos_t pos;
            fgetpos(file, &pos);
            frame_positions.push_back(pos);
            // Skip over YUV pixel data.
            fseek(file, frame_size, SEEK_CUR);
        }
        if (frame_positions.empty()) {
            LOGE("[YUVReader] Could not find any frames in the file");
            return -1;
        }
        LOGI("[YUVReader] Video has %d frames", frame_positions.size());
        return 0;
    }

    void DeInit(){
        width = 0, height = 0, frame_size = 0, fps = 0;
        frame_positions.clear();
        if(file) {
            fclose(file);
            file = nullptr;
        }
    }

    uint8_t* NextFrame(int frame_index){

        LOGI("[YUVReader] NextFrame %d totleframe=%d", frame_index, frame_positions.size());
        if(frame_index > frame_positions.size()-1){
            return nullptr;
        }
        fsetpos(file, &frame_positions[frame_index]);
        uint8_t *data = new uint8_t[frame_size];
        fread(data, 1, frame_size, file);
        return data;
    }

    size_t tokenize(const std::string& source, char delimiter, std::vector<std::string>* fields) {
        fields->clear();
        size_t last = 0;
        for (size_t i = 0; i < source.length(); ++i) {
            if (source[i] == delimiter) {
                if (i != last) {
                    fields->push_back(source.substr(last, i - last));
                }
                last = i + 1;
            }
        }
        if (last != source.length()) {
            fields->push_back(source.substr(last, source.length() - last));
        }
        return fields->size();
    }

    FILE* file;
    int width, height, frame_size, fps;

protected:
    std::vector<fpos_t> frame_positions;
};

}
#endif